package dk.itu.KrakMap;

import javax.swing.*;

/**
 * Class to represent a Viewport(map section/rectangle) of a map, denoted
 * by MapPoints of UTM-32 coordinates.
 * @author ITU-KF04-2011, Group 11
 */
public class Viewport {

    private MapPoint downLeft;
    private MapPoint upRight;

    /**
     * Setup a Viewport based on bottom-left and top-right coordinates
     * @param downLeft bottom-left coordinate
     * @param upRight top-right coordinate
     */
    public Viewport(MapPoint downLeft, MapPoint upRight) {
        this.downLeft = downLeft;
        this.upRight = upRight;
    }

    /**
     * Setup a Viewport based on bottom-left and top-right coordinates
     * @param x1 bottom-left x-coordinate
     * @param y1 bottom-left y-coordinate
     * @param x2 top-right x-coordinate
     * @param y2 top-right y-coordinate
     */
    public Viewport(double x1, double y1, double x2, double y2) {
        this(new MapPoint(x1, y1), new MapPoint(x2, y2));
    }

    /**
     * Get the bottom-left coordinate of the Viewport
     * @return the bottom-left coordinate of the Viewport
     */
    public MapPoint getDownLeft() {
        return downLeft;
    }

    /**
     * Get the top-right coordinate of the Viewport
     * @return the top-right coordinate of the Viewport
     */
    public MapPoint getUpRight() {
        return upRight;
    }

    /**
     * Get the top-left coordinate of the Viewport
     * @return the top-left coordinate of the Viewport
     */
    public MapPoint getUpLeft() {
        return new MapPoint(getDownLeft().getX(), getUpRight().getY());
    }

    /**
     * Get the bottom-right coordinate of the Viewport
     * @return the bottom-right coordinate of the Viewport
     */
    public MapPoint getDownRight() {
        return new MapPoint(getUpRight().getX(), getDownLeft().getY());
    }

    /**
     * Get the width of the Viewport
     * @return Width in meters
     */
    public double getWidth() {
        return upRight.getX() - downLeft.getX();
    }

    /**
     * Get the height of the Viewport
     * @return Height in meters
     */
    public double getHeight() {
        return upRight.getY() - downLeft.getY();
    }

    /**
     * Does the Viewport contain a given point?
     * @param point The point to be contained
     * @return True if the point is contained in the Viewport. Else false.
     */
    boolean contains(MapPoint point) {
        return getXAxis().contains(point.getX()) && getYAxis().contains(point.getY());
    }

    /**
     * Does the Viewport intersect with other Viewport?
     * @param rect The other Viewport
     * @return True if the Viewports intersects. False if not.
     */
    boolean intersects(Viewport rect) {
        return getXAxis().intersects(rect.getXAxis()) && getYAxis().intersects(rect.getYAxis());
    }

    /**
     * Pan the Viewport a given number of meters
     * @param right How much to pan right (negative means left)
     * @param up How much to pan up (negative means down)
     */
    public void pan(double right, double up) {
        this.downLeft.move(right, up);
        this.upRight.move(right, up);
    }

    /**
     * Pan the Viewport a given number of percent
     * @param percentX Percent to pan on x-axis
     * @param percentY Percent to pan on y-axis
     */
    public void pan(int percentX, int percentY) {
        double changeX = (upRight.getX() - downLeft.getX()) * (double) percentX / 100.0;
        double changeY = (upRight.getY() - downLeft.getY()) * (double) percentY / 100.0;
        pan(changeX, changeY);
    }

    /**
     * Zoom the viewport by a given percent-value
     * @param percent How much to zoom. Negative percent = zoom-out
     */
    public void zoom(int percent) {
        double changeX = (upRight.getX() - downLeft.getX()) * (double) percent / 100.0;
        double changeY = (downLeft.getY() - upRight.getY()) * (double) percent / 100.0;
        downLeft.move(changeX, -changeY);
        upRight.move(-changeX, changeY);
    }

    /**
     * Get the xAxis of the Viewport
     * @return the xAxis of the Viewport
     */
    public Interval getXAxis() {
        return new Interval(downLeft.getX(), upRight.getX());
    }

    /**
     * Get the yAxis of the Viewport
     * @return the yAxis of the Viewport
     */
    public Interval getYAxis() {
        return new Interval(downLeft.getY(), upRight.getY());
    }

    /**
     * Get the center point of the Viewport
     * @return The center point of the Viewport
     */
    public MapPoint getCenter() {
        return new MapPoint(
                getDownLeft().getX() + (getUpRight().getX() - getDownLeft().getX()) / 2,
                getDownLeft().getY() + (getUpRight().getY() - getDownLeft().getY()) / 2);
    }

    /**
     * create a copy of the ViewPort.
     * @return ViewPort
     */
    @Override
    public Viewport clone() {
        return new Viewport(downLeft.clone(), upRight.clone());
    }

    /**
     * check whether two Vievports are alike.
     * @param obj Object the object to be compared to the Viewport.
     * @return true if obj is the has the same values as the specific ViewPort.
     */
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Viewport) {
            Viewport view = (Viewport) obj;
            return getDownLeft().equals(view.getDownLeft())
                    && getUpRight().equals(view.getUpRight());
        } else {
            return false;
        }
    }

    /**
     * create a String from the viewPorts downLeft and Upright variables.
     * @return String
     */
    @Override
    public String toString() {
        return "(" + downLeft + "," + upRight + ")";
    }

    /**
     * check whether a ViewPort contains the coordinates of this ViewPort.
     * @param v ViewPort to be checked
     * @return true if this ViewPort contains downLeft && upRight of v.
     */
    boolean contains(Viewport v) {
        return contains(v.getDownLeft()) && contains(v.getUpRight());
    }
}
